package com.lanchetech.admin.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.baidu.fsg.uid.UidGenerator;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.admin.service.AnchorService;
import com.lanchetech.bean.request.AnchorReq;
import com.lanchetech.bean.request.AnchorWindowsReq;
import com.lanchetech.bean.request.DepositReq;
import com.lanchetech.bean.response.BasePageResp;
import com.lanchetech.bean.response.BaseResp;
import com.lanchetech.bean.response.ResultData;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.AnchorDepositVO;
import com.lanchetech.bean.vo.AnchorRankVO;
import com.lanchetech.bean.vo.AnchorWindowsVO;
import com.lanchetech.bean.vo.UserInfoVO;
import com.lanchetech.common.enums.*;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.service.DaoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class AnchorServiceImpl implements AnchorService {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    AnchorDepositMapper anchorDepositMapper;

    @Autowired
    DaoService daoService;

    @Autowired
    WxPayService wxPayService;

    @Resource
    UidGenerator uidGenerator;

    @Autowired
    IdentityMapper identityMapper;

    @Autowired
    UserInfoMapper userInfoMapper;

    @Autowired
    RoomMapper roomMapper;

    @Autowired
    RoomSpuMapper roomSpuMapper;

    @Value("${domain.url}")
    private String domainUrl;

    @Autowired
    AnchorWindowsMapper anchorWindowsMapper;

    @Autowired
    SkuMapper skuMapper;

    @Value("${wx.pay.appId}")
    private String miniAppId;

    @Value("${wx.app.appId}")
    private String appAppId;

    @Override
    public ResultData<BasePageResp<AnchorDepositVO>> getAnchorDepositPage(DepositReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<AnchorDepositVO> list = anchorDepositMapper.findAllPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            Map<Long, User> userMap = daoService.getUserMap(list.stream().map(AnchorDepositVO::getAnchorUserId).collect(Collectors.toList()));
            list.stream().forEach(item -> {
                item.setUserNickname(userMap.get(item.getAnchorUserId()).getNickname());
                item.setUserAvatar(userMap.get(item.getAnchorUserId()).getAvatar());
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public BaseResp auditDepositRefund(AnchorDeposit req) {
        AnchorDeposit anchorDeposit = anchorDepositMapper.selectByPrimaryKey(req.getId());
        Byte refundStatus = req.getRefundStatus();
        if (AnchorDepositRefundStatusEnum.APPLY_REFUND.getStatus().equals(anchorDeposit.getRefundStatus())) {
            if (AnchorDepositRefundStatusEnum.FULL_REFUND.getStatus().equals(refundStatus) || AnchorDepositRefundStatusEnum.PART_REFUND.getStatus().equals(refundStatus)) {

                if (PayTypeEnum.WECHAT.getType().equals(anchorDeposit.getPayWay())
                        || PayTypeEnum.ANOTHER_APY.getType().equals(anchorDeposit.getPayWay())
                        || PayTypeEnum.APP_WECHAT.getType().equals(anchorDeposit.getPayWay())
                ) {
                    if (req.getRefund().compareTo(BigDecimal.ZERO) > 0 && req.getRefund().compareTo(anchorDeposit.getAmount()) <= 0) {
                        String refundNo = Long.toString(uidGenerator.getUID());
                        anchorDeposit.setRefundNo(refundNo);
                        WxPayRefundRequest request = WxPayRefundRequest.newBuilder()
                                //订单号
                                .outTradeNo(anchorDeposit.getTradeNo())
                                //退款订单号
                                .outRefundNo(refundNo)
                                //原先总金额(分)
                                .totalFee(anchorDeposit.getAmount().multiply(new BigDecimal(100)).intValue())
                                //退款金额(分)
                                .refundFee(req.getRefund().multiply(new BigDecimal(100)).intValue())
                                //退款回调
                                .notifyUrl(domainUrl + "/admin-api/anchor/refundNotify")
                                .build();

                        WxPayRefundResult wxPayRefundResult;
                        try {
                            wxPayService.getConfig().setAppId(PayTypeEnum.APP_WECHAT.getType().equals(anchorDeposit.getPayWay()) ? appAppId : miniAppId);
                            wxPayService.getConfig().setTradeType(PayTypeEnum.APP_WECHAT.getType().equals(anchorDeposit.getPayWay()) ? "APP" : "JSAPI");


                            wxPayRefundResult = wxPayService.refund(request);
                            //判断退款信息是否正确
                            if ("SUCCESS".equals(wxPayRefundResult.getReturnCode()) && "SUCCESS".equals(wxPayRefundResult.getResultCode())) {
                                log.info("微信退款请求已经成功发送");
                                if (WxPayErrCodeEnum.SYSTEMERROR.getCode().equals(wxPayRefundResult.getErrCode())
                                        || WxPayErrCodeEnum.BIZERR_NEED_RETRY.getCode().equals(wxPayRefundResult.getErrCode())) {
                                    log.error("微信退款请求，重新发送");
                                    wxPayRefundResult = wxPayService.refund(request);
                                    if ("SUCCESS".equals(wxPayRefundResult.getReturnCode()) && "SUCCESS".equals(wxPayRefundResult.getResultCode())) {
                                        log.info("微信退款请求重新发送成功");
                                    } else {
                                        log.error("微信退款请求重新发送失败");
                                    }
                                }
                                anchorDeposit.setRefundStatus(req.getRefundStatus());
                                anchorDeposit.setRemark(req.getRemark());
                                anchorDeposit.setUpdatedAt(new Date());
                                anchorDepositMapper.updateByPrimaryKey(anchorDeposit);
                                return new BaseResp();
                            } else {
                                log.error("微信退款请求发送失败");
                            }
                        } catch (WxPayException e) {
                            log.error("微信退款接口错误信息= {}", e);
                        }
                        return new BaseResp(ResultCode.FAIL);
                    }
                }
            }else {
                anchorDeposit.setRefundStatus(req.getRefundStatus());
                anchorDeposit.setRemark(req.getRemark());
                anchorDeposit.setUpdatedAt(new Date());
                anchorDepositMapper.updateByPrimaryKey(anchorDeposit);
            }
        } else {
            return new BaseResp(ResultCode.DEPOSIT_NOT_NEED_AUDIT);
        }
        return new BaseResp();
    }


    @Override
    public String refundNotify(String xmlData) {
        wxPayService.getConfig().setAppId(xmlData.indexOf(miniAppId) > -1 ? miniAppId : appAppId);
        wxPayService.getConfig().setTradeType(xmlData.indexOf(miniAppId) > -1 ? "JSAPI" : "APP");

        WxPayRefundNotifyResult wxPayRefundNotifyResult;
        try {
            wxPayRefundNotifyResult = wxPayService.parseRefundNotifyResult(xmlData);
            // 检查微信退款成功的回调是否是他人伪造
            wxPayRefundNotifyResult.checkResult(wxPayService, "MD5", true);
        } catch (WxPayException e) {
            log.error("退款失败，失败信息:{}", e);
            return WxPayNotifyResponse.fail("退款失败");
        }

        //判断返回状态信息是否正确，是否真的退款了
        if ("SUCCESS".equals(wxPayRefundNotifyResult.getReturnCode())) {
            WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo();
            //判断退款状态
            if ("SUCCESS".equals(reqInfo.getRefundStatus())) {
                String refundNo = reqInfo.getOutRefundNo();
                refundNotifyComplete(refundNo);

                return WxPayNotifyResponse.success("退款成功！");
            }
        }
        return WxPayNotifyResponse.fail("回调有误!");
    }

    private void refundNotifyComplete(String refundNo) {
        //更新保证金的退款情况
        AnchorDeposit anchorDeposit = anchorDepositMapper.findOneByRefundNo(refundNo);
        anchorDeposit.setRefund(anchorDeposit.getAmount());
        anchorDeposit.setRefundNo(refundNo);
        anchorDeposit.setPayStatus(AnchorDepositPayStatusEnum.REFUND.getStatus());
        anchorDeposit.setCompleteTime(new Date());
        anchorDepositMapper.updateByPrimaryKey(anchorDeposit);

        //取消主播身份
        identityMapper.cancelAnchor(AnchorLevelEnum.NOT_ANCHOR.getLevel(), anchorDeposit.getAnchorUserId());
        userInfoMapper.cancelAnchor(AnchorLevelEnum.NOT_ANCHOR.getLevel(), anchorDeposit.getAnchorUserId());

        //删除当前主播的直播间和直播间下商品
        roomMapper.updateDeletedByAnchorUserId(DeletedEnum.DELETED.getStatus(), anchorDeposit.getAnchorUserId());
        List<Room> roomList = roomMapper.findAllByAnchorUserId(anchorDeposit.getAnchorUserId());
        roomSpuMapper.deleteByRoomIds(roomList.stream().map(Room::getId).collect(Collectors.toList()));
    }

    @Override
    public ResultData<BasePageResp<UserInfoVO>> getAnchorPage(AnchorReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<UserInfoVO> list = userInfoMapper.findAllAnchorPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            List<Long> anchorIds = list.stream().map(UserInfoVO::getUserId).collect(Collectors.toList());
            List<Room> openRoomList = roomMapper.findAllByAnchorUserIdsAndStatus(anchorIds, RoomStatusEnum.OPEN.getStatus());
            Map<Long, Room> openRoomMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(openRoomList)) {
                openRoomList.stream().forEach(item -> {
                    openRoomMap.put(item.getAnchorUserId(), item);
                });
            }

            List<Room> roomList = roomMapper.findAllByAnchorUserIds(anchorIds);
            Map<Long, Long> roomGoodsMap = new HashMap<>();
            roomList.stream().forEach(item -> {
                if (roomGoodsMap.containsKey(item.getAnchorUserId())) {
                    roomGoodsMap.put(item.getAnchorUserId(), roomGoodsMap.get(item.getAnchorUserId()) + item.getGoods());
                } else {
                    roomGoodsMap.put(item.getAnchorUserId(), Long.valueOf(item.getGoods()));
                }
            });

            Map<Long, User> userMap = daoService.getUserMap(anchorIds);
            list.stream().forEach(item -> {
                item.setUserNickname(userMap.get(item.getUserId()).getNickname());
                item.setUserAvatar(userMap.get(item.getUserId()).getAvatar());
                item.setIsLive(openRoomMap.containsKey(item.getUserId()));
                item.setGoodsCount(roomGoodsMap.containsKey(item.getUserId()) ? roomGoodsMap.get(item.getUserId()) : 0L);
            });

        }
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<BasePageResp<AnchorWindowsVO>> getAnchorWindowsPage(AnchorWindowsReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<AnchorWindowsVO> list = anchorWindowsMapper.findAllPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            List<Long> spuIds = list.stream().map(AnchorWindowsVO::getSpuId).collect(Collectors.toList());
            Map<Long, Spu> spuMap = daoService.getSpuMap(spuIds);

            List<Sku> skuList = skuMapper.findAllBySpuIds(spuIds);
            Map<Long, Long> spuSalesMap = new HashMap<>();
            skuList.stream().forEach(item -> {
                if (spuSalesMap.containsKey(item.getSpuId())) {
                    spuSalesMap.put(item.getSpuId(), spuSalesMap.get(item.getSpuId()) + item.getSales());
                } else {
                    spuSalesMap.put(item.getSpuId(), Long.valueOf(item.getSales()));
                }
            });

            list.stream().forEach(item -> {
                item.setSpu(spuMap.get(item.getSpuId()));
                item.setSale(spuSalesMap.get(item.getSpuId()));
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }
}


